package com.jpm.jroguefx.factories;

import com.jpm.jroguefx.Constants;
import com.jpm.jroguefx.Monster;
import com.jpm.jroguefx.types.MonsterAbility;
import com.jpm.jroguefx.utilities.PerfLogger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MonsterFactory extends ArrayList<Monster> implements Constants {

    private static MonsterFactory instance = null;
    private Logger logger = Logger.getLogger(MonsterFactory.class.getName());
    private URL location;
    private Monster currentMonster = null;
    private String element = null;

    public static synchronized MonsterFactory getInstance() {
        if (instance == null) {
            instance = new MonsterFactory();
        }
        return instance;
    }

    private MonsterFactory() {

        PerfLogger pl = new PerfLogger(this, "Monsters ");

        try {

            SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();

            final CodeSource src = GraphicsFactory.class.getProtectionDomain().getCodeSource();
            if (src != null) {
                location = src.getLocation();

                if (location.toString().toLowerCase().endsWith(jarExt)) {
                    logger.log(Level.INFO, "Loading monsters from jar...");
                    saxParser.parse(getClass().getClassLoader().getResourceAsStream(monstersFile), new MonsterHandler());

                } else {
                    logger.log(Level.INFO, "Loading monsters from file system...");
                    saxParser.parse(new File(location.toURI().toString().substring(5) + File.separator + monstersFile), new MonsterHandler());
                }
            } else {
                logger.log(Level.SEVERE, "Error loading monsters file");
            }

        } catch (ParserConfigurationException e) {
            logger.log(Level.SEVERE, "Error Configuring Parser...", e);
        } catch (SAXException e) {
            logger.log(Level.SEVERE, "Error Parsing Monster file...", e);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Error Opening Monster file...", e);
        } catch (URISyntaxException e) {
            logger.log(Level.SEVERE, "Error Locating Monster file...", e);
        }

        pl.report();
    }


    private class MonsterHandler extends DefaultHandler {

        @Override
        public void startElement(String uri, String localName, String qName,
                                 Attributes attributes) throws SAXException {

            if (qName.equalsIgnoreCase("monster")) {
                currentMonster = new Monster();
            }
        }

        @Override
        public void endElement(String uri, String localName,
                               String qName) throws SAXException {

            switch (qName.toLowerCase()) {
                case "monster":
                    add(currentMonster);
                    break;
                case "monsters":
                    break;
                case "abilities":
                    StringTokenizer st = new StringTokenizer(element);
                    while (st.hasMoreTokens()) {
                        currentMonster.setAbility(MonsterAbility.fromString(st.nextToken()), true);
                    }
                    break;
                default:

                    if (element != null) {
                        try {
                            currentMonster.getClass().getDeclaredMethod("set" + qName.substring(0, 1).toUpperCase() + qName.substring(1).toLowerCase(), String.class).invoke(currentMonster, element);
                        } catch (NoSuchMethodException e) {
                            logger.log(Level.SEVERE, "Unable to find method for tag " + qName);
                        } catch (InvocationTargetException e) {
                            logger.log(Level.SEVERE, "Method for tag " + qName + " and element " + element + " threw exception", e);
                        } catch (IllegalAccessException e) {
                            logger.log(Level.SEVERE, "Unable to access method for tag " + qName, e);
                        }
                    }
            }

        }

        @Override
        public void characters(char ch[], int start, int length) throws SAXException {

            element = new String(ch, start, length).trim();

        }

    }

    public void write() {

        try {
            File file = new File("C:/Users/jonboy/workspace/JRogueFX/src/com/jpm/jroguefx/resources/monsterList.xml");

            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();

            try (PrintWriter writer = new PrintWriter(file)) {
                writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                writer.println("<monsters>");

                for (Monster monster : this) {
                    writer.println(monster.toString());
                }

                writer.println("</monsters>");
            }
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
}
